/*  Global class for simulating the movement of particle through a 1km wind grid

 credit: All the credit for this work goes to: https://github.com/cambecc for creating the repo:
 https://github.com/cambecc/earth. The majority of this code is directly take nfrom there, since its awesome.

 This class takes a canvas element and an array of data (1km GFS from http://www.emc.ncep.noaa.gov/index.php?branch=GFS)
 and then uses a mercator (forward/reverse) projection to correctly map wind vectors in "map space".

 The "start" method takes the bounds of the map at its current extent and starts the whole gridding,
 interpolation and animation process.
 */

define(["dojo/_base/declare", "esri/geometry/Point"], function (declare, Point) {
    var WaterFlow = function (params) {
        const MAX_PARTICLE_AGE = 40;                                                 // max number of frames a particle is drawn before regeneration
        const PARTICLE_LINE_WIDTH = 1;                                               // line width of a drawn particle
        const FRAME_RATE = 15, FRAME_TIME = 1000 / FRAME_RATE;                       // desired frames per second
        const colorStyles = [
            "rgb(0,255,255)",
            "rgb(255,0,255)",
            "rgb(0,0,255)",
            "rgb(255,0,0)",
            "rgb(0,255,0)"
        ];


        var buildBounds = function (bounds, width, height) {
            var upperLeft = bounds[0];
            var lowerRight = bounds[1];
            var x = Math.round(upperLeft[0]); //Math.max(Math.floor(upperLeft[0], 0), 0);
            var y = Math.max(Math.floor(upperLeft[1], 0), 0);
            var xMax = Math.min(Math.ceil(lowerRight[0], width), width - 1);
            var yMax = Math.min(Math.ceil(lowerRight[1], height), height - 1);
            return {x: x, y: y, xMax: xMax, yMax: yMax, width: width, height: height};
        };

        var particles, animationLoop;
        var animate = function (bounds, field, extent) {
            var colorStyle = params.colorStyle || colorStyles[0];

            var particleCount = params.data.length;
            particles = particles || [];
            if (particles.length > particleCount) particles = particles.slice(0, particleCount);
            for (var i = particles.length; i < particleCount; i++) {
                var particle = JSON.parse(JSON.stringify(params.data[i]));
                particle.age = ~~(Math.random() * MAX_PARTICLE_AGE) + 0;
                particles.push(particle);
            }
            ;

            function isValue(val) {
                if (val || val == 0) {
                    return true;
                }
                ;
                return false;
            };

            function evolve() {
                particles.map(function (particle, index) {
                    if (particle.age > MAX_PARTICLE_AGE) {
                        particle.age = ~~(Math.random() * MAX_PARTICLE_AGE / 2);
                        particle.x = particle.x0;
                        particle.y = particle.y0;
                        particle.scX = null;
                        particle.scY = null;
                    }
                    ;
                    if (!isValue(particle.scX) || !isValue(particle.scY)) {
                        var point = new Point(particle.x, particle.y);
                        var screen = params.map.toScreen(point);
                        particle.scX = screen.x;
                        particle.scY = screen.y;
                    }
                    ;
                    particle.xt = particle.x + particle.dx;
                    particle.yt = particle.y + particle.dy;
                    var dPoint = new Point(particle.xt, particle.yt);
                    var dScreen = params.map.toScreen(dPoint);
                    particle.scXt = dScreen.x;
                    particle.scYt = dScreen.y;

                    particle.x = particle.xt;
                    particle.y = particle.yt;

                    particle.age++;
                });
            }

            var g = params.canvas.getContext("2d");

            function draw() {
                g.save();
                g.globalAlpha = 0.16;
                g.globalCompositeOperation = 'destination-out';
                g.fillStyle = '#000';
                g.fillRect(bounds.x, bounds.y, bounds.width, bounds.height);
                g.restore();
                g.beginPath();
                g.strokeStyle = colorStyle;
                particles.forEach(function (particle) {
                    g.moveTo(particle.scX, particle.scY);
                    g.lineTo(particle.scXt, particle.scYt);
                    particle.scX = particle.scXt;
                    particle.scY = particle.scYt;
                    if (particle.lineWidth) {
                        g.lineWidth = particle.lineWidth || PARTICLE_LINE_WIDTH;
                    }
                    ;
                });
                g.stroke();
            }

            var then = Date.now();
            (function frame() {
                animationLoop = requestAnimationFrame(frame);
                var now = Date.now()
                var delta = now - then;
                if (delta > FRAME_TIME) {
                    then = now - (delta % FRAME_TIME);
                    evolve();
                    draw();
                }
            })();
        };

        var updateData = function (data, bounds, width, height, extent) {
            delete params.data;
            params.data = data;
            if (extent)
                start(bounds, width, height, extent);
        };

        var start = function (bounds, width, height, extent) {
            stop();
            bounds = buildBounds(bounds, width, height);
            particles = [];
            animate(bounds);
        };

        var stop = function () {
            if (animationLoop) {
                cancelAnimationFrame(animationLoop);
            }
            ;
        };

        var waterFlow = {
            params: params,
            start: start,
            stop: stop,
            update: updateData
        };

        return waterFlow;
    };

    window.requestAnimationFrame = (function () {
        return window.requestAnimationFrame ||
            window.webkitRequestAnimationFrame ||
            window.mozRequestAnimationFrame ||
            window.oRequestAnimationFrame ||
            window.msRequestAnimationFrame ||
            function (callback) {
                return window.setTimeout(callback, 1000 / FRAME_RATE);
            };
    })();

    if (!window.cancelAnimationFrame) {
        window.cancelAnimationFrame = function (id) {
            clearTimeout(id);
        };
    }
    return WaterFlow;
});

